// service/common/log.go
package common

import (
	"encoding/json"
	"muziai-server/global"
	"muziai-server/models"
	"muziai-server/models/ctype"
	"muziai-server/utils/ip"
	"muziai-server/utils/jwts"
	"muziai-server/utils/log"

	"fmt"
	"github.com/gin-gonic/gin"
	"mime/multipart"
	"net/http"
	"reflect"
	"strings"
)

type Log struct {
	model        *models.LogModel
	request      *http.Request
	c            *gin.Context
	ip           string
	addr         string
	userID       uint
	title        string
	RequestBody  []byte // 请求体
	ResponseBody []byte // 响应体
	level        ctype.LogLevel
	oldLevel     ctype.LogLevel
	showRequest  bool
	showResponse bool
	contentList  []string
	itemList     []string
}

func NewLogByGin(c *gin.Context) *Log {
	_ip := c.ClientIP()
	addr := ip.GetAddr(_ip)

	return &Log{
		ip:      _ip,
		addr:    addr,
		request: c.Request,
		c:       c,
	}
}

func GetLog(c *gin.Context) *Log {
	_log, ok := c.Get("log")
	if !ok {
		return nil
	}
	log, ok := _log.(*Log)
	if !ok {
		return nil
	}
	c.Set("logShow", true)
	return log
}

func (l *Log) SetLevel(level ctype.LogLevel) {
	l.level = level
}
func (l *Log) SetTitle(title string) {
	l.title = title
}
func (l *Log) SetItemInfo(label string, value any) {
	l.setItem(ctype.InfoLogLevel, label, value)
}
func (l *Log) SetItem(label string, value any) {
	l.setItem(ctype.InfoLogLevel, label, value)
}
func (l *Log) SetItemWarn(label string, value any) {
	l.setItem(ctype.WarnLogLevel, label, value)
}
func (l *Log) SetItemError(label string, value any) {
	l.setItem(ctype.ErrorLogLevel, label, value)
}

func (l *Log) setItem(level ctype.LogLevel, label string, value any) {
	l.oldLevel = level
	t := reflect.TypeOf(value)
	var v any
	switch t.Kind() {
	case reflect.Struct, reflect.Map, reflect.Slice:
		byteData, _ := json.Marshal(value)
		v = string(byteData)
	default:
		v = value
	}
	l.itemList = append(l.itemList, fmt.Sprintf("<div class=\"log_item %s\"><div class=\"label\">%s</div><div class=\"value\">%v</div></div>",
		level.String(), label, v))
}

func (l *Log) SetLink(title, href string) {
	l.itemList = append(l.itemList, fmt.Sprintf("<div class=\"log_link\"><a href=\"%s\">%s</a></div>",
		href, title))
}
func (l *Log) SetImage(imageName string, size uint64, src string) {
	s := log.FormatBytes(size)
	l.itemList = append(l.itemList, fmt.Sprintf("<div class=\"log_image\"><div class=\"image_info\"><div class=\"name\">%s</div><div class=\"size\">%s</div></div><div class=\"image_show\"><img src=\"%s\" alt=\"\"></div></div>",
		imageName, s, src))
}
func (l *Log) SetFile(fileHeader *multipart.FileHeader, path string) {
	l.SetImage(fileHeader.Filename, uint64(fileHeader.Size), path)
}

func (l *Log) SetRequest(data []byte) {
	l.RequestBody = data
}
func (l *Log) SetResponse(data []byte) {
	l.ResponseBody = data
}

func (l *Log) ShowRequest() {
	l.showRequest = true
}
func (l *Log) ShowResponse() {
	l.showResponse = true
}

func (l *Log) Save() {
	l.save()
}
func (l *Log) save() {

	if l.showRequest {
		l.contentList = append(l.contentList,
			fmt.Sprintf("<div class=\"log_request\"><div class=\"request_head\"><div class=\"%s\">%s</div><div class=\"path\">%s</div></div> <div class=\"request_body\"><div class=\"json_body\">%s</div></div></div>",
				l.request.Method, l.request.Method, l.request.URL.String(), string(l.RequestBody)))
	}

	l.contentList = append(l.contentList, l.itemList...)

	if l.showResponse {
		l.contentList = append(l.contentList, fmt.Sprintf("<div class=\"log_response\"><div class=\"response_body\"><div class=\"json_body\">%s</div></div></div>",
			string(l.ResponseBody)))
	}

	content := strings.Join(l.contentList, "\n")

	claims, err := jwts.GetClaimsByGin(l.c)
	if err == nil && claims != nil {
		l.userID = claims.UserID
	}
	// 如果level没有显式调用
	if l.level == 0 {
		l.level = l.oldLevel
		if l.level == 0 {
			l.level = ctype.InfoLogLevel
		}
	}

	log := models.LogModel{
		Ip:      l.ip,
		Addr:    l.addr,
		UserID:  l.userID,
		Level:   l.level,
		Title:   l.title,
		Content: content,
		IsRead:  false,
		LogType: 1,
	}
	if l.model == nil {
		// 创建，第一次调用save
		global.DB.Create(&log)
		l.model = &log
	} else {
		newContent := l.model.Content + "\n" + content
		global.DB.Model(l.model).Updates(map[string]any{
			"title":   l.title,
			"level":   l.level,
			"content": newContent,
		})
	}
	// 数据归位
	l.contentList = []string{}
	l.showRequest = false
	l.showResponse = false
	l.RequestBody = []byte("")
	l.ResponseBody = []byte("")
	l.itemList = []string{}
}
